use std::collections::HashMap;

use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum DateType {
    JSON,
    STREAM,
    TEXT,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Method {
    GET,
    POST,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Options {
    headers: Value,
    response_type: DateType,
    method: Method,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ResponseJson {
    status: u16,
    data: HashMap<String, Value>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ResponsetText {
    status: u16,
    data: String,
}

use anyhow::Context;
use futures::stream::StreamExt;
use tauri::{Manager, Window};
use tokio::task;
pub enum HttpPostResult {
    HashMapResult(ResponseJson),
    StringResult(ResponsetText),
}
impl Serialize for HttpPostResult {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            HttpPostResult::HashMapResult(map) => map.serialize(serializer),
            HttpPostResult::StringResult(s) => s.serialize(serializer),
        }
    }
}

#[tauri::command]
pub async fn http_request(
    window: Window,
    url: String,
    data: String,
    options: Options,
) -> HttpPostResult {
    let client: Client = Client::new();
    let data_json = serde_json::from_str::<Value>(&data)
        .context("Failed to parse data JSON")
        .unwrap();

    let headers_json = options.headers;
    let headers_map = headers_json
        .as_object()
        .ok_or_else(|| anyhow::anyhow!("Invalid headers JSON"))
        .unwrap();

    let mut headers = HeaderMap::new();
    for (key, value) in headers_map {
        let key_bytes = key.as_bytes();
        let key = HeaderName::from_bytes(key_bytes)
            .map_err(|e| anyhow::anyhow!("Invalid header name: {}", e))
            .unwrap();
        let value_str = value
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("Header value must be a string"))
            .unwrap();
        let value = HeaderValue::from_str(value_str)
            .map_err(|_| anyhow::anyhow!("Invalid header value"))
            .unwrap();
        headers.insert(key, value);
    }
    let mut response_data = HashMap::<String, Value>::new();
    #[allow(unused_mut)]
    let mut response = None;
    match options.method {
        Method::GET => match client.get(&url)
        .body(data)
        .headers(headers.clone())
        .send().await {
            Ok(res) => response = Some(res),
            Err(e) => {
                response_data.insert("error".to_string(), Value::String(e.to_string()));
            }
        },
        Method::POST => {
            match client
                .post(&url)
                .headers(headers.clone())
                .json::<Value>(&data_json)
                .send()
                .await
            {
                Ok(res) => response = Some(res),
                Err(e) => {
                    response_data.insert("error".to_string(), Value::String(e.to_string()));
                }
            }
        }
    }
    match response {
        Some(res) => {
            let status = res.status().as_u16();
            if res.status() != 200 {
                return HttpPostResult::HashMapResult(ResponseJson {
                    data: response_data,
                    status,
                });
            }
            match options.response_type {
                DateType::JSON => {
                    return HttpPostResult::HashMapResult(ResponseJson {
                        data: res.json::<HashMap<String, Value>>().await.unwrap(),
                        status,
                    });
                }
                DateType::TEXT => {
                    return HttpPostResult::StringResult(ResponsetText {
                        data: res.text().await.unwrap(),
                        status,
                    });
                }
                DateType::STREAM => {
                    task::spawn(async move {
                        let mut stream = res.bytes_stream();
                        while let Some(item) = stream.next().await {
                            match item {
                                Ok(chunk) => {
                                    if let Ok(text) = String::from_utf8(chunk.to_vec()) {
                                        let _ = window.emit_all("event_source", text);
                                    } else {
                                        eprintln!("Failed to decode response chunk as UTF-8 text");
                                    }
                                }
                                Err(e) => {
                                    eprintln!("Error while reading response stream: {}", e);
                                    break;
                                }
                            }
                        }
                    });
                }
            }
        }
        None => {
            return HttpPostResult::HashMapResult(ResponseJson {
                data: response_data,
                status: 500,
            });
        }
    };
    return HttpPostResult::HashMapResult(ResponseJson {
        data: response_data,
        status: 500,
    });
}
